/*******************************************************************************
 *  Copyright (c) 2020-2201 Yichen Science and Tecthnology Co., Ltd.
 *
 *  File   :    socketlib.c
 *  Author :    luomin
 *  E_mail :    luomin5417@qq.com
 *  Date   :    2021/10/28
 *  Desc   :    common socket lib source code.
 *
 *  History:
 *  Date                      Modifier                    Content
******************************************************************************/
#include "common.h"
#include "socketlib.h"
#include "debuglog.h"

/**
 *  @brief  create raw socket function
 * 
 *  @param  proto the receive message socket protocol number.
 *
 *  @return socket fd.
 */
int create_raw_socket(uint16_t proto)
{
    int sockfd = 0;

    /* capture ip datagram without ethernet header */
    if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(proto)))== -1)
    {    
        printf("socket error!\n");
        return 1;
    }

    return sockfd;
}

/**
 *  @brief create raw socket function base on intface
 * 
 *  @param proto the receive message socket protocol number.
 *
 *  @return socket fd.
 */
int create_raw_socket_intf(uint16_t proto, int8_t *intf)
{
    int socketfd = 0;
    struct ifreq intfreq;
    struct sockaddr_ll lladdr;

    /* capture ip datagram without ethernet header */
    if ((socketfd = socket(PF_PACKET, SOCK_RAW, htons(proto)))== -1)
    {    
        DEBUG_LOG(LOG_FATAL, "create raw socket failed.");
        return ERROR;
    }

    memset(&intfreq, 0, sizeof(intfreq));
    strcpy((char*)(intfreq.ifr_name), (const char*)intf);

    if (ioctl(socketfd, SIOCGIFINDEX, &intfreq) < 0)
    {
        DEBUG_LOG(LOG_FATAL, "ioctl configure socket failed.");
        close(socketfd);
        return ERROR;
    }

    memset(&lladdr, 0, sizeof(lladdr));
    lladdr.sll_family = PF_PACKET;
    lladdr.sll_protocol = proto;
    lladdr.sll_ifindex  = intfreq.ifr_ifindex;
 
    if (bind(socketfd, (const struct sockaddr*)&lladdr, sizeof(lladdr)) < 0)
    {
        DEBUG_LOG(LOG_FATAL, "Bind raw socket lladdr failed.");
        close(socketfd);
        return ERROR;
    }

    return socketfd;
}

/**
 *  @brief create broadcast socket function
 * 
 *  @param void
 *
 *  @return broadcast socket fd.
 */
int create_broadcast_socket()
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) 
    {
        return ERROR;
    }

    return sock;
}

/**
 *  @brief create multicast socket base on ip addr.
 *
 *  @param void
 *
 *  @return broadcast socket fd.
 */
int create_multicast_socket(int port , char *grpaddr, char *ipaddr)
{
    int socketfd;
    int ret, bmloop = 1;
    int dwroute = 6;
    int reuse = 1;
    struct sockaddr_in localaddr;
    struct ip_mreq multicast;

    socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) ;

    /*配置端口复用*/
    ret = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,  \
                                        (int *)&reuse, sizeof(reuse));
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "set socket reuse failed. ret = %d", ret);
        close(socketfd);
        return ERROR;
    }

    memset( &localaddr, 0, sizeof(localaddr) );
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons((unsigned short)port);
    localaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    ret = bind(socketfd, (struct sockaddr *)&localaddr, sizeof(localaddr));
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "bind socket local addr failed. ret = %d", ret);
        close(socketfd);
        return ERROR;
    }

    /* 把套接字加入多播 */
    bmloop = 0;
    ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_LOOP, \
                                            (char *)&bmloop, sizeof(bmloop));
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "Socket add in multicast failed, hSocket.");
        return ERROR;
    }

    dwroute = 6;
    ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_TTL, \
                                            (char *)&dwroute, sizeof(dwroute) );
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "Socket add multicast ttl failed, hSocket.");
        return ERROR;
    }

    ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_IF,  \
                                            (char *)&ipaddr, sizeof(ipaddr));
    if ( ret == -1 )
    {
        DEBUG_LOG(LOG_FATAL, "Socket add multicast if failed, hSocket.");
        return ERROR;
    }

    memset(&multicast, 0, sizeof(multicast) );
    multicast.imr_multiaddr.s_addr = inet_addr(grpaddr);
    multicast.imr_interface.s_addr = inet_addr(ipaddr);
    ret = setsockopt(socketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
                                        (char *)&multicast, sizeof(multicast));
    if (ret < 0)
    {
        close(socketfd);
        DEBUG_LOG(LOG_FATAL, "Socket add membership failed, hSocket."
                        "ip_add_membership = %d.", IP_ADD_MEMBERSHIP);
        return ERROR;
    }

    return socketfd;
}

int create_multicast_socket_intf(int port , char *grpaddr, char *intf)
{
    int socketfd;
    int ret, bmloop = 1;
    int dwroute = 6;
    int reuse = 1;
    struct sockaddr_in localaddr;
    struct sockaddr_in *ipaddr;
    struct ip_mreq multicast;
    struct ifreq intfreq;
    char strip[16] = {0};

    socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) ;
    if (socketfd < 0)
    {
        DEBUG_LOG(LOG_FATAL, "socket init failed. fd = %d", socketfd);
        return ERROR;
    }

    strcpy(intfreq.ifr_ifrn.ifrn_name, intf);
    if (0 > (ret = ioctl(socketfd, SIOCGIFADDR, &intfreq)))
    {
        DEBUG_LOG(LOG_FATAL, "ioctl get SIOCGIFADDR failed. ret = %d", ret);
        close(socketfd);
        return ERROR;
    }

    ipaddr = (struct sockaddr_in *)&(intfreq.ifr_addr);
    sprintf(strip, "%u.%u.%u.%u", NIPQUAD(ipaddr->sin_addr));
    DEBUG_LOG(LOG_DEBUG, "%s", strip);

    /*配置端口复用*/
    ret = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR,  \
                                        (int *)&reuse, sizeof(reuse));
    ret = setsockopt(socketfd, SOL_SOCKET, SO_BINDTODEVICE, &intfreq, sizeof(intfreq));                                                                                                             
    if (0 != ret)
    { 
        DEBUG_LOG(LOG_FATAL, "set send pack bind device error.");
        close(socketfd);
        return ERROR;
    }

    memset( &localaddr, 0, sizeof(localaddr) );
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons((unsigned short)port);

    ret = bind(socketfd, (struct sockaddr *)&localaddr, sizeof(localaddr));
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "bind socket local addr failed. ret = %d", ret);
        close(socketfd);
        return ERROR;
    }

    bmloop = 0;
    ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_LOOP, \
                                            (char *)&bmloop, sizeof(bmloop));
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "set socket opt loop failed. ret = %d", ret);
        close(socketfd);
        return ERROR;
    }

    dwroute = 6;
    ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_TTL, \
                                            (char *)&dwroute, sizeof(dwroute) );
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "Socket add multicast ttl failed, hSocket.");
        close(socketfd);
        return ERROR;
    }

    ret = setsockopt(socketfd, IPPROTO_IP, IP_MULTICAST_IF,  \
                                            (const void *)&ipaddr->sin_addr, sizeof(struct in_addr));
    if (ret < 0)
    {
        DEBUG_LOG(LOG_FATAL, "Socket add multicast if failed, hSocket.");
        return ERROR;
    }

    memset(&multicast, 0, sizeof(multicast));
    multicast.imr_multiaddr.s_addr = inet_addr(grpaddr);
    multicast.imr_interface.s_addr = ipaddr->sin_addr.s_addr;
    ret = setsockopt(socketfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
                                        (char *)&multicast, sizeof(multicast));
    if (ret < 0)
    {
        close(socketfd); 
        DEBUG_LOG(LOG_FATAL, "Socket add membership failed, hSocket."
                        "ip_add_membership = %d.", IP_ADD_MEMBERSHIP);
        perror("socket");
        return ERROR;
    }

    return socketfd;
}